/*
 * Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <kernel_structs.h>
#include <offsets.h>
#include <toolchain.h>
#include <linker/sections.h>
#include <soc.h>

/* exports */
GTEXT(__soc_save_context)
GTEXT(__soc_restore_context)
GTEXT(__soc_is_irq)
GTEXT(__soc_handle_irq)
GTEXT(__soc_irq_unlock)

/* Use ABI name of registers for the sake of simplicity */

/*
 * Pulpino core has hardware loops registers that need to be saved
 * prior to handling an interrupt/exception.
 *
 * NOTE: Stack space allocation is not needed here, as already allocated at
 * architecture level with __NANO_ESF_SIZEOF value (including space for the
 * pulpino-specific registers)
 */
SECTION_FUNC(exception.other, __soc_save_context)
	/* Save hardware loop registers to stack */
	csrr t0, PULP_LPSTART0
	csrr t1, PULP_LPEND0
	csrr t2, PULP_LPCOUNT0
	sw t0, __NANO_ESF_lpstart0_OFFSET(sp)
	sw t1, __NANO_ESF_lpend0_OFFSET(sp)
	sw t2, __NANO_ESF_lpcount0_OFFSET(sp)
	csrr t0, PULP_LPSTART1
	csrr t1, PULP_LPEND1
	csrr t2, PULP_LPCOUNT1
	sw t0, __NANO_ESF_lpstart1_OFFSET(sp)
	sw t1, __NANO_ESF_lpend1_OFFSET(sp)
	sw t2, __NANO_ESF_lpcount1_OFFSET(sp)

	/* Return */
	jalr x0, ra


SECTION_FUNC(exception.other, __soc_restore_context)
	/* Restore hardloop registers from stack */
	lw t0, __NANO_ESF_lpstart0_OFFSET(sp)
	lw t1, __NANO_ESF_lpend0_OFFSET(sp)
	lw t2, __NANO_ESF_lpcount0_OFFSET(sp)
	csrw PULP_LPSTART0, t0
	csrw PULP_LPEND0, t1
	csrw PULP_LPCOUNT0, t2
	lw t0, __NANO_ESF_lpstart1_OFFSET(sp)
	lw t1, __NANO_ESF_lpend1_OFFSET(sp)
	lw t2, __NANO_ESF_lpcount1_OFFSET(sp)
	csrw PULP_LPSTART1, t0
	csrw PULP_LPEND1, t1
	csrw PULP_LPCOUNT1, t2

	/* Return */
	jalr x0, ra


/*
 * SOC-specific function to handle pending IRQ number generating the interrupt.
 *
 * The pulpino core has:
 * 1) an ICP register, which is used to clear the pending
 *    IRQ number upon an interrupt.
 * 2) an ECP register, which is used to clear the pending IRQ number
 *    that has woken up the CPU from sleep state.
 *
 * Exception number is given as parameter via register a0.
 */
SECTION_FUNC(exception.other, __soc_handle_irq)
	/* Clear exception number from the Interrupt pending register */
	li t1, PULP_ICP_ADDR
	li t2, 1
	sll t3, t2, a0
	sw t3, 0x00(t1)

	/* Clear exception number from the Event pending register */
	li t1, PULP_ECP_ADDR
	sw t3, 0x00(t1)

	/* Return */
	jalr x0, ra


/*
 * SOC-specific function to determine if the exception is the result of a
 * an interrupt or an exception
 * return 1 (interrupt) or 0 (exception)
 */
SECTION_FUNC(exception.other, __soc_is_irq)
	/* Get exception number from the mcause CSR register. */
	csrr t0, mcause
	andi t0, t0, SOC_MCAUSE_EXP_MASK

	/* if IRQ number < PULP_MIN_IRQ, not interrupt */
	li t1, PULP_MIN_IRQ
	addi a0, x0, 0
	blt t0, t1, not_interrupt
	addi a0, a0, 1

not_interrupt:
	/* return */
	jalr x0, ra
